﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CirclePanel.cs" company="Jens Horstmann">
//   Copyright (c) Jens Horstmann. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MiniMusicPlayer.Controls
{
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Windows;
    using System.Windows.Controls;

    /// <summary>
    /// The circle grid.
    /// </summary>
    public class CirclePanel : Panel
    {
        /// <summary>
        /// The items source property.
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
            "ItemsSource", 
            typeof(IEnumerable), 
            typeof(CirclePanel), 
            new PropertyMetadata(OnItemsSourcePropertyChanged));

        /// <summary>
        /// The outer radius property.
        /// </summary>
        public static readonly DependencyProperty OuterRadiusProperty = DependencyProperty.Register(
            "OuterRadius", 
            typeof(double), 
            typeof(CirclePanel), 
            new UIPropertyMetadata(
                0.0, 
                (o, e) =>
                    {
                        (o as CirclePanel).Width = (double)e.NewValue * 2;
                        (o as CirclePanel).Height = (double)e.NewValue * 2;
                    }));

        /// <summary>
        /// The inner radius property.
        /// </summary>
        public static readonly DependencyProperty InnerRadiusProperty = DependencyProperty.Register(
            "InnerRadius", 
            typeof(double), 
            typeof(CirclePanel), 
            new UIPropertyMetadata(0.0));

        /// <summary>
        /// Initializes static members of the <see cref="CirclePanel"/> class.
        /// </summary>
        static CirclePanel()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(CirclePanel), 
                new FrameworkPropertyMetadata(typeof(CirclePanel)));
        }

        /// <summary>
        /// Gets or sets the outer radius.
        /// </summary>
        public double OuterRadius
        {
            get
            {
                return (double)this.GetValue(OuterRadiusProperty);
            }

            set
            {
                this.SetValue(OuterRadiusProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the inner radius.
        /// </summary>
        public double InnerRadius
        {
            get
            {
                return (double)this.GetValue(InnerRadiusProperty);
            }

            set
            {
                this.SetValue(InnerRadiusProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the items source.
        /// </summary>
        public IEnumerable ItemsSource
        {
            get
            {
                return (IEnumerable)this.GetValue(ItemsSourceProperty);
            }

            set
            {
                this.SetValue(ItemsSourceProperty, value);
            }
        }

        /// <summary>
        /// The on items source property changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = sender as CirclePanel;
            var enumerable = control as IEnumerable;
            if (control != null && enumerable != null)
            {
                control.OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue);
            }
        }

        /// <summary>
        /// The on items source changed.
        /// </summary>
        /// <param name="oldValue">
        /// The old value.
        /// </param>
        /// <param name="newValue">
        /// The new value.
        /// </param>
        private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
        {
            // Remove handler for oldValue.CollectionChanged
            var oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged;

            if (null != oldValueINotifyCollectionChanged)
            {
                oldValueINotifyCollectionChanged.CollectionChanged -=
                    this.newValueINotifyCollectionChanged_CollectionChanged;
            }

            // Add handler for newValue.CollectionChanged (if possible)
            var newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
            if (null != newValueINotifyCollectionChanged)
            {
                newValueINotifyCollectionChanged.CollectionChanged +=
                    this.newValueINotifyCollectionChanged_CollectionChanged;
            }
        }

        /// <summary>
        /// The new value i notify collection changed_ collection changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void newValueINotifyCollectionChanged_CollectionChanged(
            object sender, 
            NotifyCollectionChangedEventArgs e)
        {
            // Do your stuff here.
        }

        /// <summary>
        /// The measure override.
        /// </summary>
        /// <param name="availableSize">
        /// The available size.
        /// </param>
        /// <returns>
        /// The <see cref="Size"/>.
        /// </returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement child in this.Children)
            {
                child.Measure(availableSize);
            }

            return new Size(2 * this.OuterRadius, 2 * this.OuterRadius);
        }

        /// <summary>
        /// The arrange override.
        /// </summary>
        /// <param name="finalSize">
        /// The final size.
        /// </param>
        /// <returns>
        /// The <see cref="Size"/>.
        /// </returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            var currentPosition = new Point(this.OuterRadius, (this.OuterRadius - this.InnerRadius) / 2);
            var childCount = this.Children.Count;
            var perAngle = 2 * Math.PI / childCount;
            double offsetX = 0.0d;
            double offsetY = 0.0d;

            for (var i = 0; i < childCount; i++)
            {
                var child = this.Children[i];
                var angle = (i + 1) * perAngle;
                offsetX = Math.Sin(angle) * (this.OuterRadius + this.InnerRadius) / 2;
                offsetY = (1 - Math.Cos(angle)) * (this.OuterRadius + this.InnerRadius) / 2;
                var childRect =
                    new Rect(
                        new Point(
                            currentPosition.X - child.DesiredSize.Width / 2, 
                            currentPosition.Y - child.DesiredSize.Height / 2), 
                        new Point(
                            currentPosition.X + child.DesiredSize.Width / 2, 
                            currentPosition.Y + child.DesiredSize.Height / 2));
                child.Arrange(childRect);
                currentPosition.X = offsetX + this.OuterRadius;
                currentPosition.Y = offsetY + (this.OuterRadius - this.InnerRadius) / 2;
            }

            return new Size(2 * this.OuterRadius, 2 * this.OuterRadius);
        }
    }
}